﻿using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Jok.Client.Infrastructure;
using Jok.Client.Loader.PackagesService;
using System.Reflection;

namespace Jok.Client.Loader
{
    public class PackagesManager
    {
        public ObservableCollection<Package> Packages { get; set; }
        public ILoaderView LoaderView { get; set; }
        public bool StorageMemoryRequired
        {
            get
            {
                return !ISManager.CheckAvailableMemory(25 * 1024 * 1024); // 25MB
            }
        }

        private ISManager ISManager = new ISManager();
        private WebClient WebClient;
        private List<string> IsolatedStorageFiles;
        private int DownloadPackagesCount = 0;
        private List<Assembly> LoadedAssemblies;
        private PackagesServiceClient client;


        public void StartLoading()
        {
            LoadedAssemblies = new List<Assembly>();

            client = new PackagesServiceClient();
            client.GetPackagesCompleted += (s, e) =>
            {
                if (e.Error != null) throw e.Error;

                // [Get packages list]
                this.Packages = e.Result;
                this.Packages.ToList().ForEach(p => p.Location = new Uri(p.FileLocation, p.IsAbsoluteLocation ? UriKind.Absolute : UriKind.Relative));
                this.Packages.ToList().ForEach(p => p.DownloadRequired = (!ISManager.Exists(DirectoryType.Packages, p.FullName)));
                DownloadPackagesCount = this.Packages.Where(p => p.DownloadRequired).Count();


                // [Delete unusablefiles]
                IsolatedStorageFiles = ISManager.GetFileNames(DirectoryType.Packages).ToList();

                this.Packages.ToList()
                    .ForEach(p => IsolatedStorageFiles.Where(f => f.Equals(p.FullName)).ToList()
                        .ForEach(f => IsolatedStorageFiles.Remove(f)));

                IsolatedStorageFiles.ForEach(f => ISManager.DeleteStream(DirectoryType.Packages, f));


                // [Start downloading packages]
                LoaderView.InitCallback();

                if (DownloadPackagesCount != 0)
                    this.Packages.Where(p => p.DownloadRequired).ToList().ForEach(p => DownloadFile(p));
                else
                {
                    LoadAllPackages();
                    LoadRunnerPackage();
                }
            };
            client.GetPackagesAsync();
        }

        private void DownloadFile(Package Package)
        {
            // [Send view callback]
            LoaderView.DownloadStartCallback(Package);

            WebClient = new WebClient();
            WebClient.DownloadProgressChanged += (s, e) => LoaderView.DownloadProgressCallback(Package, new DownloadProgressEventArgs(e));
            WebClient.OpenReadCompleted += (s, e) =>
                {
                    if (e.Error != null) throw e.Error;

                    // [Save downloaded package]
                    ISManager.SaveStream(DirectoryType.Packages, Package.FullName, e.Result);

                    // [Send view callback]
                    LoaderView.DownloadCompleteCallback(Package, new DownloadCompleteEventArgs(e));


                    DownloadPackagesCount--;
                    if (DownloadPackagesCount == 0)
                    {
                        LoadAllPackages();
                        LoadRunnerPackage();
                    }
                };
            WebClient.OpenReadAsync(Package.Location);
        }

        private void LoadAllPackages()
        {
            var PackagesNames = ISManager.GetFileNames(DirectoryType.Packages).ToList();

            // [Load packages]
            PackagesNames.ForEach(p => LoadedAssemblies.AddRange(Package.LoadPackagedAssemblies(ISManager.ReadStream(DirectoryType.Packages, p))));
        }

        private void LoadRunnerPackage()
        {
            client.GetShellParamsCompleted += (s, e) =>
                {
                    if (e.Error != null) throw e.Error;

                    var ShellAssembly = LoadedAssemblies.Where(a => a.FullName.StartsWith(e.Result[0])).FirstOrDefault();
                    if (ShellAssembly == null)
                        throw new Exception("Shell assembly not found");

                    this.LoaderView.SetShell((UIElement)ShellAssembly.CreateInstance(e.Result[1]));
                };

            client.GetShellParamsAsync();
        }
    }
}
